package minio_logic

import (
	"context"
	"errors"
	"fmt"
	"golang.org/x/sync/errgroup"
	"strings"
	"sync"
	"yunzhan/api-server/internal/model"
	"yunzhan/common/models"
	"yunzhan/common/utils"
	agent "yunzhan/rpc-server/pb"

	"yunzhan/api-server/internal/svc"
	"yunzhan/api-server/internal/types"

	"github.com/zeromicro/go-zero/core/logx"
)

type DeployMinIOLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

// NewDeployMinIOLogic 部署MinIO
func NewDeployMinIOLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeployMinIOLogic {
	return &DeployMinIOLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *DeployMinIOLogic) DeployMinIO(req *types.DeployMinIORequest) (resp *types.DeployResponse, err error) {
	if req.Nodes == nil || len(req.Nodes) == 0 {
		msg := "未配置节点信息, 无法执行MinIO部署"
		logx.Error(msg)
		return &types.DeployResponse{
			Code:    500,
			Message: msg,
		}, errors.New(msg)
	}

	agentPort := strings.Split(l.svcCtx.Config.RpcService.ListenOn, ":")[1]
	var nodeResults []*models.NodeResult
	var successNodes, failedNodes []string
	var mu sync.Mutex

	// 使用 errgroup 控制并发
	g, ctx := errgroup.WithContext(l.ctx)

	sem := make(chan struct{}, 10)

	for _, node := range req.Nodes {
		// 避免闭包引用问题
		node := node
		sem <- struct{}{}
		g.Go(func() error {
			defer func() { <-sem }()
			return l.deployNode(ctx, node, req, agentPort, &successNodes, &failedNodes, &nodeResults, &mu)
		})
	}

	if err = g.Wait(); err != nil {
		logx.Error("部署过程中出现错误: ", err)
	}

	resp = model.GenerateResponse(successNodes, failedNodes, nodeResults)
	logx.Infof("部署任务完成，响应信息: %+v", resp)
	return resp, nil
}

func (l *DeployMinIOLogic) deployNode(ctx context.Context, node *agent.NodeInfo, req *types.DeployMinIORequest, agentPort string, successNodes, failedNodes *[]string, nodeResults *[]*models.NodeResult, mu *sync.Mutex) error {
	defer func() {
		if r := recover(); r != nil {
			model.RecordNodeResult(node, fmt.Errorf("%v", r), failedNodes, nodeResults, mu)
		}
	}()

	conn, err := utils.SafeNewClient(node.Host, agentPort)
	if err != nil {
		model.RecordNodeResult(node, err, failedNodes, nodeResults, mu)
		return err
	}
	client := agent.NewMinIODeployClient(conn.Conn())

	_, err = client.DeployMinIOSingleNode(ctx, &agent.DeployMinIORequest{
		Version:     req.Version,
		NodeInfo:    node,
		ConfigInfo:  req.ConfigInfo,
		MinIOConfig: req.MinIOConfig,
		ClusterID:   req.ClusterID,
	})

	model.RecordNodeResult(node, err, successNodes, nodeResults, mu)
	return nil
}
